/**
 * @author nttdocomo
 */
define(function() {
	aries.klass("aries.util.ElementContainer", aries.Class.extend({

		childEls : [
		// empty - this solves a couple problems:
		//  1. It ensures that all classes have a childEls (avoid null ptr)
		//  2. It prevents mixins from smashing on their own childEls (these are gathered
		//      specifically)
		],

		constructor : function() {
			var me = this, childEls;

			// if we have configured childEls, we need to merge them with those from this
			// class, its bases and the set of mixins...
			if (me.hasOwnProperty('childEls')) {
				childEls = me.childEls;
				delete me.childEls;

				me.addChildEls.apply(me, childEls);
			}
		},

		/**
		 * Sets references to elements inside the component.
		 * @private
		 */
		applyChildEls : function(el, id) {
			var me = this, childEls = me.getChildEls(), baseId, childName, i, selector, value;

			baseId = (id || me.id) + '-';
			for ( i = childEls.length; i--; ) {
				childName = childEls[i];

				if ( typeof childName == 'string') {
					// We don't use Ext.get because that is 3x (or more) slower on IE6-8. Since
					// we know the el's are children of our el we use getById instead:
					value = el.find("#"+baseId + childName);
				} else {
					if (( selector = childName.select)) {
						value = Ext.select(selector, true, el.dom);
						// a CompositeElement
					} else if (( selector = childName.selectNode)) {
						value = Ext.get(Ext.DomQuery.selectNode(selector, el.dom));
					} else {
						// see above re:getById...
						value = el.getById(childName.id || (baseId + childName.itemId));
					}

					childName = childName.name;
				}

				me[childName] = value;
			}
		},

		getChildEls : function() {
			var me = this, self;

			// If an instance has its own childEls, that is the complete set:
			if (me.hasOwnProperty('childEls')) {
				return me.childEls;
			}

			// Typically, however, the childEls is a class-level concept, so check to see if
			// we have cached the complete set on the class:
			constructor = me.constructor;
			return constructor.$childEls || me.getClassChildEls(constructor);
		},

		getClassChildEls : function(cls) {
			if (cls == Backbone.View)
				return [];
			var me = this, result = cls.$childEls, childEls, i, length, forked, mixin, mixins, name, parts, proto, supr, superMixins;

			if (!result) {
				// We put the various childEls arrays into parts in the order of superclass,
				// new mixins and finally from cls. These parts can be null or undefined and
				// we will skip them later.

				supr = cls.__super__;
				if (supr) {
					supr = supr.constructor;
					parts = [supr.$childEls || me.getClassChildEls(supr)];
					// super+mixins
					superMixins = supr.prototype.mixins || {};
				} else {
					parts = [];
					superMixins = {};
				}

				proto = cls.prototype;
				mixins = proto.mixins;
				// since we are a mixin, there will be at least us
				for (name in mixins) {
					if (mixins.hasOwnProperty(name) && !superMixins.hasOwnProperty(name)) {
						mixin = mixins[name];
						parts.push(mixin.$childEls || me.getClassChildEls(mixin));
					}
				}

				parts.push(proto.hasOwnProperty('childEls') && proto.childEls);

				for ( i = 0, length = parts.length; i < length; ++i) {
					childEls = parts[i];
					if (childEls && childEls.length) {
						if (!result) {
							result = childEls;
						} else {
							if (!forked) {
								forked = true;
								result = result.slice(0);
							}
							result.push.apply(result, childEls);
						}
					}
				}

				cls.$childEls = result = ( result ? me.prune(result, !forked) : []);
			}

			return result;
		},

		prune : function(childEls, shared) {
			var index = childEls.length, map = {}, name;

			while (index--) {
				name = childEls[index];
				if ( typeof name != 'string') {
					name = name.name;
				}

				if (!map[name]) {
					map[name] = 1;
				} else {
					if (shared) {
						shared = false;
						childEls = childEls.slice(0);
					}
					aries.Array.erase(childEls, index, 1);
				}
			}

			return childEls;
		}
	}))
})
